package common.utils;

import lombok.var;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class LinqUtil extends ArrayUtil {

    public static <T> List<T> filterNotNull(List<T> items) {
        if (null == items)
            return new LinkedList<>();
        return items.stream().filter(m -> m != null).collect(Collectors.toList());
    }

    public static <T> List<T> filterNotNull(T[] items) {
        return filterNotNull(arrayToList(items));
    }

    /**
     * 筛选元素（数组）
     * 例: var items = LambdaUtil.filter(items, m -> m.getName() == "test");
     */
    public static <T> List<T> filter(List<T> items, Predicate<? super T> filterExpression) {
        items = filterNotNull(items);
        return items.stream().filter(filterExpression).collect(Collectors.toList());
    }

    public static <T> List<T> filter(T[] items, Predicate<? super T> filterExpression) {
        return filter(Arrays.asList(items), filterExpression);
    }

    /**
     * 筛选元素，取第一个
     * 例: var item = LambdaUtil.filterOne(items, m -> m.getId() == 100);
     */
    public static <T> T filterOne(List<T> items, Predicate<? super T> filterExpression) {
        return first(filter(items, filterExpression));
    }

    /**
     * 筛选元素，取第一个
     * 例: var item = LambdaUtil.filterOne(items, m -> m.getId() == 100);
     */
    public static <T> T filterOne(T[] items, Predicate<? super T> filterExpression) {
        return filterOne(ArrayUtil.arrayToList(items), filterExpression);
    }

    public static <T> List<T> filterCount(T[] items, Predicate<? super T> filterExpression, int count) {
        return select(filter(items, filterExpression), count);
    }

    public static <T> List<T> filter(T[] items, Predicate<? super T> filterExpression, int count) {
        return filterCount(items, filterExpression, count);
    }

    /***
     * 筛选属性（数组）
     * 例: var ids = LambdaUtil.map(items, Item::getId);
     */
    public static <T, R> List<R> map(List<T> items, Function<? super T, ? extends R> mapExpression) {
        items = filterNotNull(items);
        return items.stream().map(mapExpression).collect(Collectors.toList());
    }

    public static <T, R> List<R> mapDistinct(List<T> items, Function<? super T, ? extends R> mapExpression) {
        List<R> pros = map(items, mapExpression);
        return distinct(pros);
    }

    public static <T, R> List<R> map(List<T> items, Function<? super T, ? extends R> mapExpression, R ifEmptyAppend) {
        List<R> pros = map(items, mapExpression);
        if (pros.size() == 0)
            pros.add(ifEmptyAppend);
        return pros;
    }

    /**
     * 长度为0,补充null,防止in()
     */
    public static <T, R> List<R> mapNeverEmpty(List<T> items, Function<? super T, ? extends R> mapExpression) {
        return map(items, mapExpression, null);
    }

    /**
     * =mapNeverEmpty 长度为0,补充null,防止in()
     */
    public static <T, R> List<R> mapNE(List<T> items, Function<? super T, ? extends R> mapExpression) {
        return mapNeverEmpty(items, mapExpression);
    }

    public static <T, R> List<R> mapDistinctNeverEmpty(List<T> items, Function<? super T, ? extends R> mapExpression) {
        return distinct(mapNeverEmpty(items, mapExpression));
    }

    public static <T, R> List<R> mapDNE(List<T> items, Function<? super T, ? extends R> mapExpression) {
        return mapDistinctNeverEmpty(items, mapExpression);
    }

    public static <T, R> List<R> map(T[] items, Function<? super T, ? extends R> mapExpression) {
        return map(ArrayUtil.arrayToList(items), mapExpression);
    }

    /**
     * 筛选属性，取第一个
     * 例: var id = LambdaUtil.mapOne(items, Item::getId);
     */
    public static <T, R> R mapOne(List<T> items, Function<? super T, ? extends R> mapExpression) {
        return first(map(items, mapExpression));
    }

    public static <T> Integer mapMin(List<T> items, Function<? super T, ? extends Integer> mapExpression) {
        var values = map(items, mapExpression);
        var value = -1;
        for (int index = 0; index < items.size(); index++) {
            if (values.get(index) < value)
                value = values.get(index);
        }
        return value;
    }

    public static <T, R> String mapJoin(List<T> items, Function<? super T, ? extends R> mapExpression) {
        return StringUtil.join(map(items, mapExpression));
    }

    /***
     * 根据属性去重
     * 例: items = LambdaUtil.distinctByProperty(items, Item::getId);
     */
    public static <T, R> List<T> distinctByProperty(List<T> items, Function<? super T, ? extends R> mapExpression) {
        var seen = new ConcurrentHashMap<R, Boolean>();
        return items.stream().filter(m -> seen.putIfAbsent(mapExpression.apply(m), Boolean.TRUE) == null)
                .collect(Collectors.toList());
    }

    public static <T> List<T> distinct(List<T> items) {
        var seen = new ConcurrentHashMap<T, Boolean>();
        return items.stream().filter(m -> seen.putIfAbsent(m, Boolean.TRUE) == null).collect(Collectors.toList());
    }

    public static <T, R> LinkedList<T> orderBy(List<T> items, List<T> dependedItems, String orderColumn) {
        var newList = new LinkedList<T>();
        if (items.size() == 0)
            return newList;
        var field = ReflectUtil.getField(items, orderColumn);
        var fieldValues = ReflectUtil.getValues(dependedItems, field);
        for (var value : fieldValues)
            newList.addAll(LinqUtil.filter(items, m -> ReflectUtil.getValue(m, field).equals(value)));
        for (var item : items)
            if (!newList.contains(item))
                newList.add(item);
        return newList;
    }

    public static <T, R> LinkedList<T> orderBy(List<T> items, List<T> dependedItems, FieldExpression<? super T, ? extends R> orderColumn) {
        return orderBy(items, dependedItems, orderColumn.getName());
    }

    public static <T, R> void removeBy(List<T> items, FieldExpression<? super T, ? extends R> fieldExpression, Object value) {
        var removeItems = filter(items, m -> fieldExpression.apply(m).equals(value));
        items.removeAll(removeItems);
    }
}
